package org.sasha.roulette.chart;

import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;

import javax.inject.Inject;

import org.sasha.roulette.data.ChartHelper;
import org.sasha.roulette.data.RouletteConstant;

import com.github.peholmst.mvp4vaadin.Presenter;
import com.invient.vaadin.charts.Color.RGB;
import com.invient.vaadin.charts.InvientCharts;
import com.invient.vaadin.charts.InvientCharts.DecimalPoint;
import com.invient.vaadin.charts.InvientCharts.Series;
import com.invient.vaadin.charts.InvientCharts.SeriesType;
import com.invient.vaadin.charts.InvientCharts.XYSeries;
import com.invient.vaadin.charts.InvientChartsConfig;
import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.AxisTitle;
import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.Tick;
import com.invient.vaadin.charts.InvientChartsConfig.AxisBase.TickmarkPlacement;
import com.invient.vaadin.charts.InvientChartsConfig.CategoryAxis;
import com.invient.vaadin.charts.InvientChartsConfig.ColumnConfig;
import com.invient.vaadin.charts.InvientChartsConfig.HorzAlign;
import com.invient.vaadin.charts.InvientChartsConfig.NumberYAxis;
import com.invient.vaadin.charts.InvientChartsConfig.PointConfig;
import com.invient.vaadin.charts.InvientChartsConfig.Title;
import com.invient.vaadin.charts.InvientChartsConfig.XAxisDataLabel;
import com.invient.vaadin.charts.InvientChartsConfig.YAxis;

@SuppressWarnings("serial")
public class ChartPresenter extends Presenter<ChartView> {

    private final ChartHelper helper;

    @Inject
    public ChartPresenter(final ChartHelper helper) {
        super();
        this.helper = helper;
    }

    public LinkedHashSet<Series> getOccurenceSeries() {
        final LinkedHashSet<Series> seriesSet = new LinkedHashSet<InvientCharts.Series>();
        final List<Double> points = helper.getOccurenceList();

        final ColumnConfig columnCfg = new ColumnConfig();
        columnCfg.setPointWidth(25);
        columnCfg.setShadow(false);
        columnCfg.setShowInLegend(false);
        
        final PointConfig redPointCfg = new PointConfig(new RGB(255, 0, 0));
        final PointConfig blackPointCfg = new PointConfig(new RGB(0, 0, 0));
        final PointConfig zeroPointCfg = new PointConfig(new RGB(0, 128, 0));
        
        final XYSeries series = new XYSeries("", SeriesType.COLUMN, columnCfg);
        final Integer[] order = RouletteConstant.ORDER;
        for (int idx = 0; idx < points.size(); ++idx) {
            Double pointValue = points.get(idx);
            PointConfig cfg = zeroPointCfg;
            if (Arrays.binarySearch(RouletteConstant.RED_VAL, order[idx]) > -1) {
                cfg = redPointCfg;
            } else if (Arrays.binarySearch(RouletteConstant.BLACK_VAL, order[idx]) > -1) {
                cfg = blackPointCfg;
            }
            series.addPoint(new DecimalPoint(series, pointValue.intValue(), cfg));
        }
        seriesSet.add(series);
        return seriesSet;
    }
    
    public LinkedHashSet<Series> getParitySeries() {
        final LinkedHashSet<Series> seriesSet = new LinkedHashSet<InvientCharts.Series>();
        int odds = helper.getOddCount();
        int evens = helper.getEvenCount();
        
        final ColumnConfig columnCfg = new ColumnConfig();
        columnCfg.setShadow(false);
        columnCfg.setPointWidth(60);
        columnCfg.setShowInLegend(false);
        
        final PointConfig oddCfg = new PointConfig(new RGB(255, 0, 0));
        final PointConfig evenCfg = new PointConfig(new RGB(0, 0, 0));
        
        final XYSeries series = new XYSeries("", SeriesType.COLUMN, columnCfg);
        
        series.addPoint(new DecimalPoint(series, evens, evenCfg), new DecimalPoint(series, odds, oddCfg));
        seriesSet.add(series);
        return seriesSet;
    }
    
    
    public LinkedHashSet<Series> getThirdsSeries() {
        final LinkedHashSet<Series> seriesSet = new LinkedHashSet<InvientCharts.Series>();

        final ColumnConfig columnCfg = new ColumnConfig();
        columnCfg.setPointWidth(60);
        columnCfg.setShadow(false);
        columnCfg.setShowInLegend(false);
        
        final PointConfig firstPointCfg = new PointConfig(new RGB(255, 0, 0));
        final PointConfig secondPointCfg = new PointConfig(new RGB(0, 0, 0));
        final PointConfig thirdPointCfg = new PointConfig(new RGB(0, 128, 0));
        
        final XYSeries series = new XYSeries("Первая", SeriesType.COLUMN, columnCfg);

        int[] thirdsCount = helper.getThirdsFrequencies();
        series.addPoint(
                new DecimalPoint(series, thirdsCount[0], firstPointCfg), 
                new DecimalPoint(series, thirdsCount[1], secondPointCfg), 
                new DecimalPoint(series, thirdsCount[2], thirdPointCfg));
        seriesSet.add(series);
        return seriesSet;
    }

    public InvientChartsConfig configureThirdsChart() {
        return doConfig(new Context(50d, "Трети", "Количество", "Трети", RouletteConstant.THIRDS_LABELS));
    }
    
    public InvientChartsConfig configureOccurenceChart() {
        return doConfig(new Context(5d, "Ячейка", "Частота", "Частоты значений", RouletteConstant.LABELS));
    }
    
    public InvientChartsConfig configureParityChart() {
        return doConfig(new Context(50d, "Четные/Нечетные", "Колличество", "Четные/Нечетные", RouletteConstant.PARITY_LABELS));
    }
    
    private InvientChartsConfig doConfig(final Context context) {
        final InvientChartsConfig cfg = new InvientChartsConfig();
        cfg.getGeneralChartConfig().setType(SeriesType.COLUMN);
        cfg.getGeneralChartConfig().setReflow(true);
        final CategoryAxis xAxis = new CategoryAxis();
        xAxis.setCategories(Arrays.asList(context.getCategories()));
        xAxis.setTitle(new AxisTitle(context.getxAxisTitle()));
        
        final Tick xAxisTick = new Tick();
        xAxisTick.setPlacement(TickmarkPlacement.ON);
        xAxis.setTick(xAxisTick);
        
        final XAxisDataLabel xLabel = new XAxisDataLabel();
        xLabel.setAlign(HorzAlign.CENTER);
        xAxis.setLabel(xLabel);
        
        final YAxis yAxis = new NumberYAxis();
        final Tick tick = new Tick();
        tick.setInterval(context.getTickStep());
        yAxis.setTick(tick);
        
        yAxis.setTitle(new AxisTitle(context.getyAxisTitle()));

        cfg.addXAxes(xAxis);
        cfg.addYAxes(yAxis);

        final ColumnConfig colCfg = new ColumnConfig();
        cfg.addSeriesConfig(colCfg);

        final Title title = new Title();
        title.setText(context.getChartTitle());
        title.setAlign(HorzAlign.LEFT);
        cfg.setTitle(title);

        return cfg;        
    }
    
    private static class Context implements Serializable {
        
        private final String xAxisTitle;
        private final String yAxisTitle;
        private final String chartTitle;
        private final String[] categories;
        private double tickStep;
        
        public Context(double tickStep, String x, String y, String c, String[] cats) {
            this.xAxisTitle = x;
            this.yAxisTitle = y;
            this.chartTitle = c;
            this.categories = cats;
            this.tickStep = tickStep;
        }
        
        public double getTickStep() {
            return tickStep;
        }
        
        public String getChartTitle() {
            return chartTitle;
        }
        
        public String[] getCategories() {
            return categories;
        }
        
        public String getxAxisTitle() {
            return xAxisTitle;
        }
        
        public String getyAxisTitle() {
            return yAxisTitle;
        }
    }
}
